package hyl.ext.web.ms;

import java.io.File;
import java.util.Map;

import hyl.base.cache2.ExToken;
import hyl.base.net.IpgReader;
import hyl.base.net.nio.NIOSocketCli;
import hyl.core.MyDate;
import hyl.core.MyFun;
import hyl.core.io.MyFile;
import hyl.core.io.MyPath;
import hyl.core.net.MySocket;

import hyl.core.net.pg.Netpg2Msgs1;
import hyl.core.run.IDo1;
import hyl.core.run.MyRun;
import hyl.core.safe.MyRsa;
import hyl.ext.base.ILogin;
import hyl.ext.base.MySession;
import hyl.ext.base.SessionFactory;

/**
 * microserver manager(ms) 微服务管理 组件
 * 
 * 微服务注册,微服务删除,服务重启,服务停止,服务启动,服务升级,服务查看
 * 
 * MsB 会伴随 应用系统启动 注意: 发送的字节必须小于1400
 * 
 * @author 37798955@qq.com
 *
 */
public class TMsB extends TMs {
	static final String cfgfile = "msb";
	NIOSocketCli _cli;
	static TMsB _mb = null;
	/**
	 * 必须在TMsb绑定时赋值
	 */
	public ILogin ssologin = null;

	/**
	 * 单例模式
	 * 
	 * @return
	 */

	public static TMsB getInstance() {
		if (_mb == null) {
			_mb = new TMsB();
		}
		return _mb;
	}

	/**
	 * 实例化
	 */
	public TMsB() {
		super();
		load密钥();
		_binfo = new BInfo();
		File file = MyFile.find(cfgfile);

		if (file == null) {
			file = MyFile.openFile(MyPath.getRootPath() + cfgfile);
		}
		MyFun.print(file.getAbsoluteFile());
		_binfo.init(file);
		_binfo.save();
		// 最好不要线程开在这里 ,放到外部更好
		// MyRun.start守护线程(() -> {
		conn();
		// });
	}

//	public static void test() {
//		InfoMsm im = new InfoMsm();
//		im.init();
//		System.out.println(JSON.toJSONString(im));
//		im.save();
//		Netpg2Msgs1 _npg = new Netpg2Msgs1();
//		// byte[] eb=im.encode();
//		// MyFun.print(eb.length,MyByte.bytesToHex(eb));
//		_npg.set(0, null, im.encode());
//		byte[] bb = _npg.getSendBytes();
//		// MyFun.print(bb.length,MyByte.bytesToHex(bb));
//		Netpg2Msgs1 netpg2 = new Netpg2Msgs1();
//		netpg2.ini(bb);
//		byte[] bb2 = netpg2.getBb携参();
//		InfoMsm im2 = new InfoMsm();
//		im2._信息 = InfoMsm.decode(bb2);
//		System.out.println(JSON.toJSONString(im2));
//	}

	/**
	 * 接入服务中心
	 */
	void conn() {

		// MyFun.print(_m属性.a_ip);
		_cli = new NIOSocketCli(MySocket.getIPAddress(_binfo.a_ip));
		_cli.conn();
		// MyFun.print("MsmClient conn():",中心);
		IDo1<IpgReader> handle = new IDo1<IpgReader>() {
			@Override
			public void run(IpgReader nr) {
				byte[] data;
				while ((data = nr.pullData()) != null) {
					// 防止并发异常
					Netpg2Msgs1 np = new Netpg2Msgs1(data);
					switch (np.cmd) {// 请求连接的应答
					case I未知:
						break;
					case I注册:
						_binfo.connected(np.msgs);
						ping();
						break;
					case IPING:
						// ping 指令
						break;
					case IERR:
						// err 反馈
						break;
					case I内部请求:
						try {
							resA2(np);
						} catch (Exception e) {
							e.printStackTrace();
						}
						break;
					case I内部应答:
						resB3(np);
					case I请求处理:// 收到对方的消息
						if (on收到消息处理 != null)
							MyRun.start用户线程(() -> {
								on收到消息处理.run(np);
							});
						break;
					case I请求应答:// 收到对方的消息并应答
						if (on收到消息处理并反馈 != null)
							MyRun.start用户线程(() -> {
								Netpg2Msgs1 np1 = on收到消息处理并反馈.ask(np);
								if (np1 == null)
									return;
								np1.setCmd(I请求应答);
								_cli.send(np1.toSendBytes());
							});
						break;
					default:
						// 关闭通知
						MyFun.print("关闭...");
						_cli.close();
					}
				}
			}
		};
		// _cli.set_IDo断开处理函数(handle);
		_cli.set_IDo接收处理函数(handle);
		_cli.startReader();// 启动连续接收
		Netpg2Msgs1 np = new Netpg2Msgs1();
		// 连接指令包 0 开头

		String mm = MyFun.join(_binfo.getAppid(), ",", _binfo.keystr);
		// MyFun.print(mm);
		mm = MyRsa.f公钥加密uri(K公钥, mm);
		// String mqString= MyRsa.f私钥解密uri(K私钥, mm);
		// System.out.println("-----------------");
		// System.out.println(mqString);
		np.set(I注册, _binfo.encode(), mm);
		// MyFun.print("MsmClient conn() send im:", _npg.getBb携参().length);
		// MyFun.print("MsmClient conn() send im:", JSON.toJSONString(im));
		_cli.send(np.toSendBytes());
	}

	/**
	 * 维持心跳
	 */
	protected void ping() {
		MyRun.start用户线程(() -> {
			boolean flag = true;
			int i = 0;
			while (flag) {
//				 if ( Thread.currentThread().isInterrupted() ) {
//		                System.out.println("i has interputed");
//		                break;
//		            }
				if (!_cli.isConnected()) {
					return;
				}
				_binfo.ping();
				Netpg2Msgs1 np = new Netpg2Msgs1();
				if (i < 4320) {// 每4320 相当于系统每天 更新一次密钥
					np.set(IPING, null, _binfo.getAppid());
					i++;
				} else {
					byte[] mm = _binfo.aes加密(_binfo.upKey());
					np.set(IPING, mm);
					i = 0;
				}
				// MyFun.print("ping1", flag, _cli.isConnected());
				// 网络断开以后, 通道的连接状态依然是true
				// 此时发送是不会抛出异常
				// 只有发送失败以后才会修改连接状态为false
				flag = _cli.send(np.toSendBytes());
				MyDate.sleeps(5);
				// MyFun.print("ping2", flag, _cli.isConnected());
			}
			MyFun.print("已关闭");
		});
	}

	/**
	 * 服务异常
	 */
	public void errOrExit(String err) {
		Netpg2Msgs1 np = new Netpg2Msgs1();
		if (err == null)
			err = "未知错误";
		_binfo.err(err);
		_binfo.save();
		np.set(IERR, null, _binfo.getAppid(), err);
		_cli.send(np.toSendBytes());
	}

///////////////////////////////////////////////////
	// 第二步 回答对方有无这个token
	public void resA2(Netpg2Msgs1 npg) {
		Netpg2Msgs1 np = new Netpg2Msgs1();
		String cmd = npg.msgs[0];
		String key = npg.msgs[1];
		// MyFun.print(npg.msgs.length);
		// MyFun.print(npg.msgs);
		if (S验证令牌.equals(cmd)) {
			boolean flag = _etlist.contain_1(key);
			np.set(I内部应答, null, S验证令牌, key, flag ? "Y" : "N");

		} else if (S获取令牌.equals(cmd)) {
			ExToken et = createExToken();
			// MyFun.printJson(et);
			np.set(I内部应答, null, S获取令牌, key, et.getId());
		} else if (S获取会话.equals(cmd)) {			
			MySession session = get有效会话(npg.msgs[2]);
			// MyFun.printJson(session);
			np.set(I内部应答, null, S获取会话, key, session.getTokenId());
		}
		_cli.send(np.toSendBytes());
	}

	public MySession get有效会话(String mw) {
		String users[] = _binfo.aes解密64(mw).split(",");
		Integer userid=MyFun.str2int(users[1]);
		String ip=users[2];
		MySession mysess = SessionFactory.getSsoSession(userid, ip);
		Map<String, Object> userb = getUserByID(userid);
		ssologin.setLoginSession(mysess, userb);
		return mysess;
	}

	// 第三步 结合对方回答修改阻塞器 第一步就有答案了
	public void resB3(Netpg2Msgs1 npg) {
		String cmd = npg.msgs[0];
		String key = npg.msgs[1];
		String value = npg.msgs[2];
		_阻塞map.put(cmd + key, value);
	}

	/**
	 * 缺陷还不支持并发, 一定要确保 命令和参数的正确
	 * 
	 * @param cmd
	 * @param param
	 * @return
	 */
	public String req向A提问(String cmd, String param) {
		if (MyFun.isBlank(cmd) || MyFun.isBlank(param)) {
			return null;
		}
		Netpg2Msgs1 np = new Netpg2Msgs1();
		np.set(I内部请求, null, cmd, param);
		String key = cmd + param;
		_阻塞map.open(key);
		// 注意:!!!!发送并收到反馈结果的时候,可能blockvalue 还没有创建好
		_cli.send(np.toSendBytes());
		try {
			String value = _阻塞map.get(key);
			// MyFun.print("2222");
			return value;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	///////////////////////////
	// 第二步 回答对方有无这个token

	// 第三步 结合对方回答修改阻塞器 第一步就有答案了

	// 第一步 问对方有没有这个token

	public void sendMsg(byte[] bytes, String... msgs) {
		Netpg2Msgs1 np = new Netpg2Msgs1();
		np.set(I请求处理, bytes, msgs);
		_cli.send(np.toSendBytes());
	}

	public void request(byte[] bytes, String... msgs) {
		Netpg2Msgs1 np = generate反馈(bytes, msgs);
		_cli.send(np.toSendBytes());
	}

}
